package com.coder.widget.guide

import android.app.Activity
import android.graphics.*
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.RelativeLayout

/**
 * @description 引导视图
 *      创建完成后可直接调用 show() 展示，添加到对应的 Activity的decorView上
 *
 * @author yuhus
 * @date 2021/4/6 14:47
 */
class GuideView private constructor(private val builder: Builder) : RelativeLayout(builder.targetView.context) {

    private var showing = false

    /**
     * 目标区域
     */
    private var mTargetRect = Rect()

    /**
     * 目标路径
     */
    private var mTargetPath: Path? = null

    /**
     * 当前布局显示的区域
     */
    private var mRect = Rect()

    /**
     * 当前布局显示的坐标
     */
    private var mPoint = Point()

    /**
     * 遮罩颜色
     */
    private val mMaskColor = builder.maskColor

    init {
        // 需要绘制
        setWillNotDraw(false)
        builder.targetView.post {
            measureTargetView()
            // decorView中需要
            onLayoutHintView()
        }
        // hintView存在添加进来
        builder.hintView?.let {
            addView(it)
        }
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        if (builder.targetOnClickListener != null && ev.action == MotionEvent.ACTION_UP && isTouchOnTargetView(ev)) {
            builder.targetOnClickListener?.onClick(builder.targetView)
            return true
        }
        return super.dispatchTouchEvent(ev)
    }

    private fun isTouchOnTargetView(ev: MotionEvent): Boolean {
        return mTargetRect.contains(ev.rawX.toInt(), ev.rawY.toInt())
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)
        if (changed) {
            measureGlobalVisible()
            // Fragment中需要
            onLayoutHintView()
        }
    }

    /**
     * 计算当前View在屏幕上的坐标
     */
    private fun measureGlobalVisible() {
        val location = IntArray(2)
        getLocationOnScreen(location)
        mPoint.set(location[0], location[1])
        getGlobalVisibleRect(mRect)
    }

    private fun measureTargetView() {
        builder.targetView.getGlobalVisibleRect(mTargetRect)
        mTargetPath = builder.targetPath ?: Path().also {
            it.addRect(RectF(mTargetRect), Path.Direction.CW)
        }
    }

    private fun onLayoutHintView() {
        val layoutParams = builder.hintView?.layoutParams as? LayoutParams
        builder.layoutParams?.let {
            layoutParams?.width = it.width
            layoutParams?.height = it.height
        }
        when (builder.direction) {
            Direction.TOP -> {
                gravity = Gravity.BOTTOM
                layoutParams?.bottomMargin = mRect.height() - mTargetRect.top + mPoint.y
            }
            Direction.BOTTOM -> {
                gravity = Gravity.TOP
                layoutParams?.topMargin = mTargetRect.bottom - mPoint.y
            }
            Direction.LEFT -> {
                gravity = Gravity.RIGHT
                layoutParams?.rightMargin = mRect.width() - mTargetRect.left + mPoint.x
            }
            Direction.RIGHT -> {
                gravity = Gravity.LEFT
                layoutParams?.leftMargin = -mRect.left + mTargetRect.right
            }
        }
        builder.hintView?.layoutParams = layoutParams
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.save()
        canvas.translate(-mPoint.x.toFloat(), -mPoint.y.toFloat())
        mTargetPath?.let {
            canvas.clipPath(it, Region.Op.DIFFERENCE)
        }
        canvas.drawColor(mMaskColor)
        canvas.restore()
    }


    fun show() {
        if (showing) {
            return
        }
        showing = true
        if (context is Activity) {
            val activity = context as Activity
            (activity.window.decorView as FrameLayout).addView(this)
        }
    }

    fun hide() {
        if (!showing) {
            return
        }
        showing = false
        if (context is Activity) {
            val activity = context as Activity
            (activity.window.decorView as FrameLayout).removeView(this)
        }
    }

    class Builder {
        internal lateinit var targetView: View
        internal var targetPath: Path? = null
        internal var targetOnClickListener: OnClickListener? = null
        internal var hintView: View? = null
        internal var direction = Direction.BOTTOM
        internal var layoutParams: ViewGroup.LayoutParams? = null
        internal var maskColor = Color.TRANSPARENT


        /**
         * 设置引导的目标视图
         */
        fun setTargetView(targetView: View): Builder {
            this.targetView = targetView
            return this
        }

        /**
         * 设置目标区域
         */
        fun setTargetPath(targetPath: Path): Builder {
            this.targetPath = targetPath
            return this
        }

        /**
         * 设置提示视图
         */
        fun setHintView(hintView: View): Builder {
            this.hintView = hintView
            return this
        }

        /**
         * 设置提示视图相对于目标视图的方位
         */
        fun setHintViewDirection(direction: Direction): Builder {
            this.direction = direction
            return this
        }

        /**
         * 设置提示视图的布局属性
         */
        fun setHintViewLayoutParams(params: ViewGroup.LayoutParams): Builder {
            this.layoutParams = params
            return this
        }

        /**
         * 设置蒙层颜色，默认透明
         */
        fun setMaskColor(maskColor: Int): Builder {
            this.maskColor = maskColor
            return this
        }

        fun build(): GuideView {
            return GuideView(this)
        }
    }

    enum class Direction {
        LEFT, TOP, RIGHT, BOTTOM
    }

    /**
     * 定义目标控件的形状
     * 共3种。圆形，椭圆，带圆角的矩形（可以设置圆角大小），不设置则默认是圆形
     */
    enum class Shape {
        CIRCULAR, ELLIPSE, RECTANGULAR
    }


    companion object {
        /**
         * 获取内置椭圆形 path
         */
        fun getOvalPath(oval: RectF) = Path().also {
            it.addOval(oval, Path.Direction.CW)
        }

        /**
         * 获取圆形 path
         * @param radius 半径
         */
        fun getCirclePath(x: Float, y: Float, radius: Float) = Path().also {
            it.addCircle(x, y, radius, Path.Direction.CW)
        }

        /**
         * 获取矩形 path
         */
        fun getRectPath(rect: RectF) = Path().also {
            it.addRect(rect, Path.Direction.CW)
        }

        /**
         * 获取圆角矩形 path
         */
        fun getRoundRectPath(rect: RectF, radius: Float) = Path().also {
            it.addRoundRect(rect, radius, radius, Path.Direction.CW)
        }
    }
}